package com.sky.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * JWT工具类，用于生成和解析JWT令牌。
 * @author Administrator
 */
@Data
@Component
@ConfigurationProperties(prefix = "sky.jwt")
public class JwtUtils {

    /**
     * 令牌过期时间，单位：秒。
     */
    private long expire;

    /**
     * 用于签名的秘钥。
     */
    private String secret;

    /**
     * 令牌在HTTP请求头中的字段名。
     */
    private String header;

    /**
     * 生成JWT令牌。
     *
     * @param username 令牌的主体，即用户名。
     * @return 生成的JWT字符串。
     */
    public String generateToken(String username) {
        Date nowDate = new Date();
        // 7天过期
        Date expireDate = new Date(nowDate.getTime() + 1000 * expire);

        return Jwts.builder()
                .setHeaderParam("typ", "JWT")
                .setSubject(username)
                .setIssuedAt(nowDate)
                .setExpiration(expireDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 解析JWT令牌，获取其中的声明。
     *
     * @param jwt 待解析的JWT字符串。
     * @return JWT中的声明信息，如果解析失败则返回null。
     */
    public Claims getClaimsByToken(String jwt) {
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(jwt)
                    .getBody();
            return claims;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 判断JWT令牌是否过期。
     *
     * @param claims JWT中的声明信息。
     * @return 如果令牌过期返回true，否则返回false。
     */
    public boolean isTokenExpired(Claims claims) {
        return claims.getExpiration().before(new Date());
    }

    /**
     * 验证 token 是否有效
     * @param token JWT 的 token
     * @param userDetails 从数据库中查询出来的用户信息（需要自定义 UserDetailsService 和 UserDetails）
     * @return token 是否有效 true：有效 false：无效
     */
    public boolean validateToken(String token, UserDetails userDetails){
        // 从 token 中获取用户名
        Claims claimsByToken = getClaimsByToken(token);
        String username = claimsByToken.getSubject();
        // 条件一：用户名不为 null
        // 条件二：用户名和 UserDetails 中的用户名一致
        // 条件三：token 未过期
        return username.equals(userDetails.getUsername()) && !isTokenExpired(claimsByToken);
    }
}
